﻿using System.Drawing.Drawing2D;
using System.IO;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using A14_Ex06_Noam_300476694_Shai_300380698.Properties;

namespace A14_Ex06_Noam_300476694_Shai_300380698
{
    public partial class DesignedBoardForm : Form
    {
        #region Messages
        public const string k_EnterWidth = "Please enter the width of the board (minimum {0}, maximum {1})";
        public const string k_EnterHeight = "Please enter the height of the board (minimum {0}, maximum {1})";
        public const string k_DeclareWinner = "{0} Won!!";
        public const string k_DeclareTie = "Tie!!";
        public const string k_Rematch = "Another Round?";
        #endregion

        #region Consts
        private const int k_BoardStartWidthPixel = 18;
        private const int k_BoardStartHeightPixel = 20;
        private const int k_SpaceBetweenMenu = 20;
        #endregion

        #region Data Members
        private Bitmap m_ImageEmptyCell = Properties.Resources.EmptyCell;
        private Bitmap m_ImageRedCoin = Properties.Resources.CoinRed;
        private Bitmap m_ImageYellowCoin = Properties.Resources.CoinYellow;
        private Bitmap m_ImageFullRedCell = Properties.Resources.FullCellRed;
        private Bitmap m_ImageFullYellowCell = Properties.Resources.FullCellYellow;

        private AboutForm m_GameProperties;
        private GameLogic m_GameLogic;
        private List<Label> m_CoinsInput;
        private BoardCell[,] m_BoardCells;
        private bool m_ChangeResolutionNextGame = false;

        private PictureBox m_CurrentCoin;
        private bool m_InAnimation;
        private int m_TopPositionOfNewCell;
        private uint? m_CurrentMoveColumn;
        private uint? m_CurrentMoveRow;
        #endregion

        #region Properties
        public int WidthOfCoinCell
        {
            get
            {
                return Resources.EmptyCell.Size.Width;
            }
        }

        public int HeightOfCoinCell
        {
            get
            {
                return Resources.EmptyCell.Size.Height;
            }
        }

        public int WidthOfBoard
        {
            get
            {
                return (WidthOfCoinCell * (int)m_GameProperties.Cols) + 2;
            }
        }

        public int HeightOfBoard
        {
            get
            {
                return (HeightOfCoinCell * (int)m_GameProperties.Rows) + HeightOfCoinCell + 2;
            }
        }

        public Bitmap CurrentCoinBitmap
        {
            get
            {
                return m_GameLogic.CurrentPlayerNumber == 1 ? m_ImageRedCoin : m_ImageYellowCoin;
            }
        }

        public Bitmap CurrentCellBitmap
        {
            get
            {
                return CurrentCoinBitmap == m_ImageRedCoin ? m_ImageFullRedCell : m_ImageFullYellowCell;
            }
        }

        public string CurrentPlayerName
        {
            get
            {
                return m_GameLogic.CurrentPlayerNumber == 1 ? m_GameProperties.Player1 : m_GameProperties.Player2;
            }
        }
        #endregion

        public DesignedBoardForm()
        {
            InitializeComponent();
            this.Show();
            m_GameProperties = new AboutForm();
            m_GameLogic = new GameLogic();

            if (m_GameProperties.ShowDialog() == DialogResult.OK)
            {
                initAllBoardProps();
                this.ShowDialog();
            }
        }

        private void initAllBoardProps()
        {
            m_BoardCells = new BoardCell[m_GameProperties.Cols, m_GameProperties.Rows];
            m_CoinsInput = new List<Label>((int)m_GameProperties.Cols);
            initGame();
            this.Hide();
        }

        private void initGame()
        {
            initPlayer2();
            initBoard();
            updateScoreLabels();
            this.ClientSize = new System.Drawing.Size(((int)m_GameProperties.Cols * 70) + 20, (int)m_GameProperties.Rows * 100);                
        }

        private void initBoard()
        {
            if (m_GameLogic.Board == null)
            {
                m_GameLogic.Board = new Board(m_GameProperties.Cols, m_GameProperties.Rows);
            }

            m_GameLogic.InitBoard();

            initCoinsInput();
            enableAllColumnLabels();
            initBoardButtons();
            initGameBoard();

            m_GameLogic.ResetCurrentPlayer();
            m_InAnimation = false;
            m_CurrentMoveColumn = null;
            m_CurrentMoveRow = null;

            updateScoreLabels();

            initCursorToCurrentPlayer();
        }

        private void initPlayer2()
        {
            m_GameLogic.Player2 = new Player(m_GameProperties.PlayerType, 2);
        }

        private void initCursorToCurrentPlayer()
        {
            if (m_CurrentCoin != null)
            {
                Controls.Remove(m_CurrentCoin);
            }

            PictureBox currCoin = new PictureBox();
            currCoin.Size = new Size(60, 60);
            currCoin.BackColor = Color.Transparent;
            currCoin.Image = CurrentCoinBitmap;
            GraphicsPath currCoinPath = new GraphicsPath();
            currCoinPath.AddEllipse(currCoin.ClientRectangle);
            currCoin.Region = new Region(currCoinPath);
            currCoin.BringToFront();
            Controls.Add(currCoin);
            m_CurrentCoin = currCoin;
        }

        private void initGameBoard()
        {
            panelGameBoard.Location = new System.Drawing.Point(k_BoardStartWidthPixel - 1, k_BoardStartHeightPixel + k_SpaceBetweenMenu - 1);
            panelGameBoard.Size = new System.Drawing.Size(WidthOfBoard, HeightOfBoard);
            panelGameBoard.BackColor = System.Drawing.Color.Thistle;
            panelGameBoard.BorderStyle = BorderStyle.FixedSingle;
            panelGameBoard.SendToBack();
            updateCurrentPlayerLable();
        }

        private void updateCurrentPlayerLable()
        {
            string currentPlayer = string.Format("Current Player: {0}", CurrentPlayerName);
            toolStripCurrentPlayerLabel.Text = currentPlayer;
        }

        private void initCoinsInput()
        {
            int PreviousLocationHeight = k_BoardStartHeightPixel + k_SpaceBetweenMenu;
            int PreviousLocationWidth = k_BoardStartWidthPixel;

            if (m_CoinsInput.Count > 0)
            {
                removeCoinsInputControls();
                m_CoinsInput = new List<Label>((int)m_GameProperties.Cols);
            }

            for (int i = 0; i < m_GameProperties.Cols; i++)
            {
                m_CoinsInput.Add(new Label());
                m_CoinsInput[i].Location = new System.Drawing.Point(PreviousLocationWidth, PreviousLocationHeight);
                m_CoinsInput[i].Size = new System.Drawing.Size(WidthOfCoinCell, HeightOfCoinCell);
                m_CoinsInput[i].Enabled = true;

                m_CoinsInput[i].BringToFront();
                m_CoinsInput[i].Parent = this.panelGameBoard;
                m_CoinsInput[i].BackColor = Color.Thistle;

                m_CoinsInput[i].MouseMove += new System.Windows.Forms.MouseEventHandler(this.panelGameBoard_MouseMove);

                this.Controls.Add(m_CoinsInput[i]);
                this.m_CoinsInput[i].Click += new EventHandler(CoinInput_Click);
                PreviousLocationWidth += WidthOfCoinCell;
            }
        }

        private void removeCoinsInputControls()
        {
            foreach (Control coinInput in m_CoinsInput)
            {
                this.Controls.Remove(coinInput);
                coinInput.Dispose();
            }
        }

        private void initBoardButtons()
        {
            int PreviousLocationHeight = k_BoardStartHeightPixel + k_SpaceBetweenMenu + HeightOfCoinCell;
            int PreviousLocationWidth = k_BoardStartWidthPixel;

            Cell[,] cellsOnBoard = m_GameLogic.GetBoardCellsMat();

            if (m_BoardCells[0, 0] != null)
            {
                removeBoardCellsControls();
                m_BoardCells = new BoardCell[m_GameProperties.Cols, m_GameProperties.Rows];
            }

            for (int i = 0; i < m_GameProperties.Cols; i++)
            {
                for (int j = 0; j < m_GameProperties.Rows; j++)
                {
                    m_BoardCells[i, j] = new BoardCell(m_ImageEmptyCell);
                    m_BoardCells[i, j].Location = new System.Drawing.Point(PreviousLocationWidth, PreviousLocationHeight);
                    m_BoardCells[i, j].Text = string.Empty;

                    m_BoardCells[i, j].BringToFront();
                    m_BoardCells[i, j].Parent = this.panelGameBoard;
                    m_BoardCells[i, j].BackColor = Color.Transparent;

                    BitmapRegion.CreateControlRegion(m_BoardCells[i, j]);

                    m_BoardCells[i, j].MouseMove += new System.Windows.Forms.MouseEventHandler(this.panelGameBoard_MouseMove);

                    this.Controls.Add(m_BoardCells[i, j]);

                    PreviousLocationHeight += HeightOfCoinCell;
                }

                PreviousLocationWidth += WidthOfCoinCell;
                PreviousLocationHeight = k_BoardStartHeightPixel + k_SpaceBetweenMenu + HeightOfCoinCell;
            }
        }

        private void removeBoardCellsControls()
        {
            foreach (Control boardCell in m_BoardCells)
            {
                this.Controls.Remove(boardCell);
                boardCell.Dispose();
            }
        }

        private void CoinInput_Click(object sender, EventArgs e)
        {
            base.OnClick(e);

            uint column = 0;
            bool k_HumanPlayer = true;

            if (m_InAnimation)
            {
                return;
            }

            Label clickedCoinInput = sender as Label;
            column = (uint)m_CoinsInput.IndexOf(clickedCoinInput) + 1;

            if (m_GameLogic.CurrentPlayer.Type == ePlayerType.Human)
            {
                checkMoveAndMakeAnimation(k_HumanPlayer, sender);
            }
            else
            {
                checkMoveAndMakeAnimation(!k_HumanPlayer, null);
            }
        }

        private void checkMoveAndMakeAnimation(bool i_Human, object i_PickedColumnLabel)
        {
            uint? pickedRow = null;
            uint? pickedColumn = null;
            string errorMessage = string.Empty;
            bool successMove = false;

            if (i_Human)
            {
                pickedColumn = (uint)m_CoinsInput.IndexOf(i_PickedColumnLabel as Label) + 1;

                if (pickedColumn != null)
                {
                    pickedRow = m_GameLogic.FindRowInAvailableMovesByColumn((uint)pickedColumn);
                }
            }
            else
            {
                m_GameLogic.MakeAutomaticMove(m_GameLogic.CurrentPlayer, out pickedColumn, out pickedRow);

                i_PickedColumnLabel = m_CoinsInput[(int)pickedColumn - 1]; // get the label control for the computer's chosen move
            }

            if (pickedRow != null)
            {
                successMove = m_GameLogic.SetSquareOnBoard(m_GameLogic.CurrentPlayer, (uint)pickedColumn, (uint)pickedRow, out errorMessage);
                if (successMove)
                {
                    makeCoinAnimationAndMakeMove(i_PickedColumnLabel, (uint)pickedColumn, (uint)pickedRow);
                }
            }
            else
            {
                errorMessage = Board.k_ErrorIllegalMove;
                successMove = false;
            }

            if (!successMove)
            {
                if (errorMessage != string.Empty)
                {
                    showMessageBoxWithOK(errorMessage);
                }
            }
        }

        private void matchIsOver(bool i_Tie)
        {
            bool playerWon = false;

            if (i_Tie)
            {
                playerWon = false;
            }
            else
            {
                m_GameLogic.CurrentPlayer.RaisePlayerScore();
                updateScoreLabels();
                playerWon = true;
            }

            askForRematch(playerWon);
        }

        private void updateScoreLabels()
        {
            string player1Lable = string.Format("{0}: {1}", m_GameProperties.Player1, m_GameLogic.Player1.Score);
            string player2Lable = string.Format("{0}: {1}", m_GameProperties.Player2, m_GameLogic.Player2.Score);
            string scoreLable = string.Format("{0} , {1}", player1Lable, player2Lable);
            toolStripScoreLabel.Text = scoreLable;
        }

        private void askForRematch(bool i_PlayerWon)
        {
            string message = string.Empty;

            if (i_PlayerWon == true)
            {
                string winner = string.Format(k_DeclareWinner, getPlayerNameByNumber(m_GameLogic.CurrentPlayerNumber));
                message = string.Format("{0}{1}{2}", winner, System.Environment.NewLine, k_Rematch);
                if (m_GameLogic.WinningMoves != null)
                {
                    blinkWinningCells(m_GameLogic.WinningMoves);
                }
            }
            else
            {
                message = string.Format("{0}{1}{2}", k_DeclareTie, System.Environment.NewLine, k_Rematch);
            }

            m_GameLogic.ChangeCurrentPlayer();
            bool rematch = showMessageBoxWithYesNo(message);

            if (rematch)
            {
                timer2.Stop();
                if (!m_ChangeResolutionNextGame)
                {
                    initBoard();
                }
                else
                {
                    m_GameLogic.Board = null;
                    initGame();
                }
            }
            else
            {
                endGame();
            }
        }

        private string getPlayerNameByNumber(uint i_PlayerNumber)
        {
            string result = string.Empty;

            if (i_PlayerNumber == 1)
            {
                result = m_GameProperties.Player1;
            }
            else if (i_PlayerNumber == 2)
            {
                result = m_GameProperties.Player2;
            }
            else
            {
                result = string.Empty;
            }

            return result;
        }

        private void endGame()
        {
            this.Close();
        }

        private bool showMessageBoxWithYesNo(string i_Message)
        {
            bool result = false;

            if (MessageBox.Show(i_Message, "Message", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == System.Windows.Forms.DialogResult.Yes)
            {
                result = true;
            }
            else
            {
                result = false;
            }

            return result;
        }

        private void showMessageBoxWithOK(string i_Message)
        {
            MessageBox.Show(i_Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void makeCoinAnimationAndMakeMove(object sender, uint i_PickedColumn, uint i_PickedRow)
        {
            m_InAnimation = true;
            m_CurrentCoin.Top = ((Control)sender).Top;
            m_CurrentCoin.Left = ((Control)sender).Left + 3;
            m_CurrentCoin.SendToBack(); // send the coin behind all controls except...
            ((Control)sender).SendToBack(); // send label back
            panelGameBoard.SendToBack(); // send panel back
            m_TopPositionOfNewCell = m_CurrentCoin.Top + (int)(HeightOfCoinCell * i_PickedRow);

            m_CurrentMoveColumn = i_PickedColumn;
            m_CurrentMoveRow = i_PickedRow;

            timer1.Start();
        }

        private void drawCurrentCell(uint i_PickedColumn, uint i_PickedRow)
        {
            GraphicsPath currCoinPath = new GraphicsPath();
            currCoinPath.AddRectangle(m_BoardCells[(uint)i_PickedColumn, (uint)i_PickedRow].ClientRectangle);
            m_BoardCells[(uint)i_PickedColumn, (uint)i_PickedRow].Region = new Region(currCoinPath);
            m_BoardCells[(uint)i_PickedColumn, (uint)i_PickedRow].Region = new Region(currCoinPath);
            m_BoardCells[(uint)i_PickedColumn, (uint)i_PickedRow].BackgroundImage = CurrentCellBitmap;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (m_CurrentCoin.Top >= m_TopPositionOfNewCell)
            {
                drawCurrentCell((uint)m_CurrentMoveColumn - 1, (uint)m_CurrentMoveRow - 1);
                timer1.Stop();
                makeMoveAfterAnimationEnded();
                initCursorToCurrentPlayer();
                m_InAnimation = false;
                updateCurrentPlayerLable();
            }
            else
            {
                m_CurrentCoin.Top += 20;
            }
        }

        private void makeMoveAfterAnimationEnded()
        {
            bool playerWonAfterTurn = false;
            const bool MatchEndedWithTie = true;

            if (m_CurrentMoveColumn != null && m_CurrentMoveRow != null)
            {
                disableColumnIfColumnIsFull((uint)m_CurrentMoveColumn);

                playerWonAfterTurn = m_GameLogic.CheckIfCurrentPlayerWon((uint)m_CurrentMoveColumn, (uint)m_CurrentMoveRow);
                if (playerWonAfterTurn)
                {
                    matchIsOver(!MatchEndedWithTie);
                } 
                else if (m_GameLogic.IsBoardFull())
                {
                    matchIsOver(MatchEndedWithTie);
                }
                else
                {
                    m_GameLogic.ChangeCurrentPlayer();
                }

                m_CurrentMoveColumn = null;
                m_CurrentMoveRow = null;
            }
        }

        private void disableColumnIfColumnIsFull(uint i_ColumnToCheck)
        {
            if (m_GameLogic.IsColumnFull(i_ColumnToCheck))
            {
                m_CoinsInput[(int)i_ColumnToCheck - 1].Enabled = false;
            }
        }

        private void enableAllColumnLabels()
        {
            for (int i = 0; i < m_CoinsInput.Count; i++ )
            {
                m_CoinsInput[i].Enabled = true;
            }
        }

        private void panelGameBoard_MouseMove(object sender, MouseEventArgs e)
        {
            if (!m_InAnimation)
            {
                m_CurrentCoin.Top = e.Y + ((Control)sender).Top + 10;
                m_CurrentCoin.Left = e.X + ((Control)sender).Left + 10;
                Controls.SetChildIndex(m_CurrentCoin, 0);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.endGame();
        }

        private void startANewGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!m_ChangeResolutionNextGame)
            {
                initBoard();
            }
            else
            {
                m_GameLogic.Board = null;
                initGame();
            }
        }

        private void startANewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_GameLogic.InitPlayersScores();
            initGame();
        }

        private void howToPlayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FileStream fileStream = null;
            const string k_Path = @"C:\FourInARowHelp.txt";
            const string k_MessageTitle = "How to play?";
            const string k_ErrorMessage = @"There is no file under: 'C:\FourInARowHelp.txt'";

            try
            {
                fileStream = new FileStream(k_Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                StreamReader reader = new StreamReader(fileStream);
                string content = reader.ReadToEnd();

                MessageBox.Show(content, k_MessageTitle);
            }
            catch (FileNotFoundException)
            {
                MessageBox.Show(k_ErrorMessage);
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutGameForm tempForm = new AboutGameForm();
            tempForm.ShowDialog();
        }

        private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_GameProperties.ShowDialog() == DialogResult.OK)
            {
                string msgToShow = string.Format("Start New Game?");
                string caption = "4 In A row";
                if (MessageBox.Show(msgToShow, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
                {
                    m_GameLogic.Board = null;
                    initGame();
                }
                else
                {
                    m_ChangeResolutionNextGame = true;
                    msgToShow = string.Format("New board size will take effect {0}on the next game.", Environment.NewLine);
                    caption = "4 In A row";
                    MessageBox.Show(msgToShow, caption, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        private void blinkWinningCells(List<Point> i_WinningPoints)
        {
            int player1 = 1;
            int player2 = 2;
            this.timer2 = new Timer();
            this.timer2.Interval = 500;
            bool didFlash = false;
            this.timer2.Tick += new EventHandler(delegate
            {
                if (!didFlash)
                {
                    foreach (Point point in i_WinningPoints)
                    {
                        if (m_GameLogic.CurrentPlayerNumber == player2)
                        {
                            this.m_BoardCells[point.X, point.Y].BackgroundImage = Resources.PinkFullCellRed;
                        }
                        else if (m_GameLogic.CurrentPlayerNumber == player1)
                        {
                            this.m_BoardCells[point.X, point.Y].BackgroundImage = Resources.PinkFullCellYellow;
                        }
                    }
                }
                else
                {
                    foreach (Point point in i_WinningPoints)
                    {
                        if (m_GameLogic.CurrentPlayerNumber == player2)
                        {
                           this.m_BoardCells[point.X, point.Y].BackgroundImage = Resources.FullCellRed;
                        }
                        else if (m_GameLogic.CurrentPlayerNumber == player1)
                        {
                            this.m_BoardCells[point.X, point.Y].BackgroundImage = Resources.FullCellYellow;
                        }
                    }
                }

                didFlash = !didFlash;
            });

            this.timer2.Start();
        }
    }
}